package com.damai.push;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.ssl.HttpsURLConnection;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.citywithincity.api.ApiBuildConfig;
import com.citywithincity.api.ApiConfig;
import com.citywithincity.interfaces.IDestroyable;
import com.citywithincity.utils.DateTimeUtil;
import com.citywithincity.utils.HttpUtil;
import com.citywithincity.utils.HttpsUtil;
import com.citywithincity.utils.JsonUtil;
import com.citywithincity.utils.MapUtil;

public class JPushModel {

	private static final String GATE = "https://api.jpush.cn/v3/push";

	private String auth;

	public JPushModel(String appKey, String appSec) {
		String base64 = Base64.encodeBase64String(
				new StringBuilder().append(appKey).append(":").append(appSec).toString().getBytes());
		auth = new StringBuilder("Basic ").append(base64).toString();
	}
	
	public static interface PushReport{
		void onPushStart(PushBuilder builder);
		void onPushEnd(PushBuilder builder, Map<String, Object> data);
		void onPushError(PushBuilder builder, IOException e);
	}

	public static class PushBuilder implements IDestroyable {

		private Map<String, Object> push = new HashMap<String, Object>();

		private PushReport report;
		
		public PushBuilder setReport(PushReport report){
			this.report = report;
			return this;
		}
		
		public PushBuilder platformAll() {
			push.put("platform", "all");
			return this;
		}
		
		public PushBuilder setData(Object data){
			this.extra = data;
			return this;
		}
		/**
		 * 附带的数据
		 */
		private Object extra;
		@SuppressWarnings("unchecked")
		public <T> T getData(){
			return (T) extra;
		}

		public PushBuilder setRegId(String regId) {
			/**
			 * 推送目标
			 */
			Map<String, Object> audience = new HashMap<String, Object>();
			audience.put("registration_id", new String[] { regId });
			push.put("audience", audience);

			return this;
		}
		
		public PushBuilder setAlias(String alias){
			Map<String, Object> audience = new HashMap<String, Object>();
			audience.put("alias", new String[] { alias });
			push.put("audience", audience);

			return this;
		}
		
		/**
		 * data为非json字符串
		 * @param data  可以为map\bean
		 * @return
		 */
		public PushBuilder message(Object data){
			Map<String, Object> content = new HashMap<String, Object>();
			content.put("msg_content", JsonUtil.toJson(data));
			push.put("message", content);
			return this;
		}
		
		public PushBuilder audienceAll(){
			push.put("audience", "all");
			return this;
		}

		/**
		 * 设置内容
		 * @param title
		 * @param data   非json字符串，可以为Map  bean
		 * @return
		 */
		public PushBuilder setContent(String title, Object data) {
			Map<String, Object> notification = new HashMap<String, Object>();
			notification.put("alert", title);

			Map<String, Object> android = new HashMap<String, Object>();
			android.put("alert", title);
			android.put("extras", data);

			Map<String, Object> ios = new HashMap<String, Object>();
			ios.put("alert", title);
			ios.put("extras", data);

			notification.put("android", android);
			notification.put("ios", ios);

			push.put("notification", notification);

			return this;
		}
		
		public PushBuilder setOptions(){
			Map<String, Object> options = new HashMap<String, Object>();
			options.put("sendno", DateTimeUtil.getTime());
			options.put("apns_production", !ApiConfig.debug());
		//	options.put("apns_production", false);
			push.put("options", options);
			return this;
		}
		
		public String getResult(){
			return JsonUtil.toJson(push);
		}

		public void repoartStart() {
			if(report!=null){
				report.onPushStart(this);
			}
		}

		public void repoartSuccess(Map<String, Object> data) {
			if(report!=null){
				report.onPushEnd(this,data);
			}
		}

		public void repoartException(IOException e) {
			if(report!=null){
				report.onPushError(this,e);
			}
		}

		@Override
		public void destroy() {
			if(push!=null){
				push.clear();
				push = null;
			}
			report = null;
			
		}

	}
	
	private static PushBuilder builder(){
		return new PushBuilder();
	}
	
	public Map<String, Object> sendByDeviceID(String title, Map<String, Object> data, String alias) throws IOException {
		return send(builder().platformAll().setAlias(alias).setContent(title, data).setOptions());
	}
	public Map<String, Object> sendByPushId(String title, Map<String, Object> data, String regId) throws IOException {
		return send(builder().platformAll().setRegId(regId).setContent(title, data).setOptions());
	}
	public Map<String, Object> sendNotificationByAlias(String title, Map<String, Object> data, String alias) throws IOException {
		return send(builder().platformAll().setAlias(alias).setContent(title, data).setOptions());
	}
	
	public static PushBuilder helperSendByDeviceID(String title, Map<String, Object> data, String deviceId){
		return builder().platformAll().setAlias(deviceId).setContent(title, data).setOptions();
	}
	public static PushBuilder helperSendByPushId(String title, Map<String, Object> data, String pushId){
		return builder().platformAll().setRegId(pushId).setContent(title, data).setOptions();
	}
	public static PushBuilder helperSendNotificationByAlias(String title, Map<String, Object> data, String alias){
		return builder().platformAll().setAlias(alias).setContent(title, data).setOptions();
	}
	
	public static PushBuilder helperSendNotificationAll(String title, Map<String, Object> data){
		return builder().platformAll().audienceAll().setContent(title, data).setOptions();
	}
	public static PushBuilder helperSendNotificationByPushId(String title, Map<String, Object> data,
			String pushId) {
		return builder().platformAll().setRegId(pushId).setContent(title, data).setOptions();
	}

	
	public static PushBuilder helperSendMessageByRegId(Map<String, Object> data, String regId) {
		//从库表中查找regId
		return builder().platformAll().setRegId(regId).message(data).setOptions();
	}

	public static PushBuilder helperSendMessageByAlias( Map<String, Object> data, String alias){
		return builder().platformAll().setAlias(alias).message(data).setOptions();
	}
	
	@SuppressWarnings("unchecked")
	private Map<String, Object> sendRawData(PushBuilder builder) throws IOException{
		String content = builder.getResult();
		logger.info("Push Start :" + content);
		
		builder.repoartStart();

		URL postUrl = new URL(GATE);
		HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();

		connection.setDoInput(true);
		connection.setUseCaches(false);
		connection.setInstanceFollowRedirects(true);
		connection.setRequestMethod("POST");

		connection.setRequestProperty("Charset", ApiBuildConfig.DEFAULT_ENCODING);
		connection.setRequestProperty("Content-Type", "application/json");
		connection.setRequestProperty("Authorization", auth);
		connection.setDoOutput(true);
		connection.setConnectTimeout(HttpUtil.CONNECT_TIMEOUT);
		connection.setReadTimeout(HttpUtil.READ_TIMEOUT);

		if(connection instanceof HttpsURLConnection) {
			HttpsUtil.setConnections((HttpsURLConnection)connection);
		}
		
		
		HttpUtil.send(connection, content);

		InputStream inputStream;
		
		
		if (connection.getResponseCode() == 200) {
			inputStream = connection.getInputStream();
			
		} else {
			inputStream = connection.getErrorStream();
			
		}
		Map<String, Object> data = JsonUtil.read(inputStream);
		Map<String, Object> error = (Map<String, Object>) data.get("error");
		if(error!=null){
			Object code = error.get("code");
			if((Integer)code == 2002){	
				Map<String, String> headers = new HashMap<String, String>();
		        for (Entry<String, List<String>> entry : connection.getHeaderFields().entrySet()) {
					String key = entry.getKey();
					if(key!=null){
						headers.put(key, entry.getValue().get(0));
					}
				}
				String reset = headers.get("X-Rate-Limit-Reset");
				try {
					
					Thread.sleep(Integer.parseInt(reset) * 1000);
					return sendRawData(builder);
				} catch (NumberFormatException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
		}
		
		return data;
	}
	
	private Map<String, Object> trySend(PushBuilder builder) throws IOException{
		Map<String, Object> data = sendRawData(builder);
		logger.info("Push end:" + MapUtil.toString(data));
		return data;
	}
	
	public Map<String, Object> send(PushBuilder builder) throws IOException{
		try{
			Map<String, Object> data = trySend(builder);
			builder.repoartSuccess(data);
			return data;
		}catch(IOException e){
			builder.repoartException(e);
			logger.error("Push error",e);
			throw e;
		}finally{
			builder.destroy();
		}
		
	}
	private static Log logger = LogFactory.getLog(JPushModel.class);

	

	
	

}
